﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using YasharEl.Infrastructure.ApplicationModel.Modularity.Bootstrap;
using YasharEl.Infrastructure.ApplicationModel.Bootstrap.Configuration;
using System.Configuration;
using YasharEl.Infrastructure.ResourcesManagement.Configuration;
using YasharEl.Infrastructure.ResourcesManagement;
using YasharEl.Infrastructure.ObjectFactory;
using YasharEl.Infrastructure.ResourcesManagement.Providers;
using YasharEl.Infrastructure.Diagnostics.Logging;

namespace YasharEl.Infrastructure.ApplicationModel.Bootstrap.Extensions
{
    public class ResourcesManagementBootstrapperExtension : ModuleComponentBootstrappingExtension, IConsumeConfigurationSection
    {
        #region Properties

        public ResourcesManagementConfig ResourcesManagementConfigSection { get; private set; }

        #endregion

        #region IConsumeConfigurationSection Implementations

        public void Apply(ConfigurationSection section)
        {
            ResourcesManagementConfigSection = section as ResourcesManagementConfig;
        }

        #endregion

        #region BaseComplexBootstrappingExtension Abstract Overrides

        public override string Describe()
        {
            return "Extension which init and configure resources management cross cutting infrastructure feature.";
        }

        #endregion

        #region BaseComplexBootstrappingExtension Configure Override

        public override void Configure()
        {
            base.Configure();

            if (ResourcesManagementConfigSection == null ||
                ResourcesManagementConfigSection.ResourceProviders == null ||
                ResourcesManagementConfigSection.ResourceProviders.Count == 0)
            {
                throw new InvalidSystemConfigurationException("Resource management cross cutting feature requested but not correctly configured. Check you config files.");
            }

            if (ResourcesManagementConfigSection != null)
            {
                if (ResourcesManagementConfigSection.ResourceProviders != null &&
                    ResourcesManagementConfigSection.ResourceProviders.Count > 0)
                {
                    foreach (ResourceProviderConfig resourceProviderConfig in ResourcesManagementConfigSection.ResourceProviders)
                    {
                        Logger.Info("Configuring {0} resource provider with name {1} ...",
                            resourceProviderConfig.GetType().FullNameToString(),
                            resourceProviderConfig.Name
                        );

                        IResourceProvider currentResourceProvider = null;

                        currentResourceProvider = registerAssemblyResourceProvider(resourceProviderConfig);
                        if (currentResourceProvider == null)
                        {
                            currentResourceProvider = registerDatabaseResourceProvider(resourceProviderConfig);
                        }

                        if (ResourcesManagementConfigSection.ResourceProviders.DefaultResourceProvider == resourceProviderConfig.Name &&
                            currentResourceProvider != null)
                        {
                            // Register this resource provider as default resource provider
                            // When requested without name container will resolve this provider.
                            Container.DependencyRegistrar.Register<IResourceProvider>(currentResourceProvider, true);

                            Logger.Info("Resource provider({0}) configured and registered to container...", currentResourceProvider.GetType());
                        }
                    }

                    Type resourceManagerType = typeof(IResourceManager);

                    FactoryMethod factoryMethod = new FactoryMethod(
                            (genericType, resolver, requestedType, key)
                                =>
                            {
                                if (!string.IsNullOrEmpty(key))
                                    return resolver.Resolve<IResourceProvider>(key).CreateResourceManager();
                                else
                                    return resolver.Resolve<IResourceProvider>().CreateResourceManager();
                            }
                    );
                    // Register resource manager factory method to container.
                    Container.DependencyRegistrar.RegisterFactory(resourceManagerType, factoryMethod);

                    Logger.Info("Resource manager({0}) configured and registered to container...", resourceManagerType);
                }
                else
                {
                    Logger.Warn("Resources management cross cutting feature enabled but no providers supplied.");
                }
            }
            else
            {
                Logger.Warn("Resources management cross cutting feature enabled but no configuration file specified.");
            }
        }

        #endregion

        #region ModuleComponentBootstrappingExtension Overrides

        public override string SectionName
        {
            get { return ResourcesManagementConfig.ResourcesManagementConfigDefaultSectionName; }
        }

        #endregion

        #region Private Methods

        private IResourceProvider registerDatabaseResourceProvider(ResourceProviderConfig resourceProviderConfig)
        {
            DataResourceProvider dataResourceProvider = null;
            DatabaseResourceProviderConfig dataResourceProviderConfig = resourceProviderConfig as DatabaseResourceProviderConfig;
            if (dataResourceProviderConfig != null)
            {
                dataResourceProvider = new DataResourceProvider(
                    dataResourceProviderConfig.DataProvider,
                    dataResourceProviderConfig.ConnectionString,
                    dataResourceProviderConfig.ResourceBaseName,
                    Container.DependencyResolver.Resolve<ILogger<ResourceProvider>>()
                );
                Container.DependencyRegistrar.Register<IResourceProvider>(
                    dataResourceProvider,
                    dataResourceProviderConfig.Name,
                    true
                );
                Container.DependencyRegistrar.Register<DataResourceProvider>(
                    dataResourceProvider,
                    dataResourceProviderConfig.Name,
                    true
                );
            }
            return dataResourceProvider;
        }

        //private IResourceProvider registerXmlResourceProvider(ResourceProviderConfig resourceProviderConfig)
        //{
        //    XmlResourceProvider xmlResourceProvider = null;
        //    XmlResourceProviderConfig xmlResourceProviderConfig = resourceProviderConfig as XmlResourceProviderConfig;
        //    if (xmlResourceProviderConfig != null)
        //    {
        //        xmlResourceProvider = new XmlResourceProvider(
        //            xmlResourceProviderConfig.FullPath,
        //            xmlResourceProviderConfig.Name,
        //            xmlResourceProviderConfig.XmlExtension
        //        );
        //        Container.DependencyRegistrar.Register<IResourceProvider>(
        //            xmlResourceProvider,
        //            xmlResourceProviderConfig.Name,
        //            true
        //        );
        //        Container.DependencyRegistrar.Register<XmlResourceProvider>(
        //            xmlResourceProvider,
        //            xmlResourceProviderConfig.Name,
        //            true
        //        );
        //    }
        //    return xmlResourceProvider;
        //}

        private IResourceProvider registerAssemblyResourceProvider(ResourceProviderConfig resourceProviderConfig)
        {
            AssemblyResourceProvider assemblyResourceProvider = null;
            AssemblyResourceProviderConfig assemblyResourceProviderConfig = resourceProviderConfig as AssemblyResourceProviderConfig;
            if (assemblyResourceProviderConfig != null)
            {
                assemblyResourceProvider = new AssemblyResourceProvider(
                    assemblyResourceProviderConfig.ResourceAssemblyName,
                    assemblyResourceProviderConfig.Name,
                    assemblyResourceProviderConfig.AssemblyExtension,
                    Container.DependencyResolver.Resolve<ILogger<ResourceProvider>>()
                );
                Container.DependencyRegistrar.Register<IResourceProvider>(
                    assemblyResourceProvider,
                    assemblyResourceProviderConfig.Name,
                    true
                );
                Container.DependencyRegistrar.Register<AssemblyResourceProvider>(
                    assemblyResourceProvider,
                    assemblyResourceProviderConfig.Name,
                    true
                );
            }
            return assemblyResourceProvider;
        }

        #endregion
    }
}
